package com.minelx.scriptbuilder.parse.annotation;

import com.minelx.scriptbuilder.script.attribute.clazz.Class;
import com.minelx.scriptbuilder.script.attribute.function.Function;
import com.minelx.scriptbuilder.script.attribute.function.Params;
import com.minelx.scriptbuilder.script.attribute.value.IAttribute;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;

import static java.util.stream.Collectors.toList;

public class AnnotatedClassParser {

	public AttributeBundle parse(Object annotatedSource) {
		Class result = new Class();
		String className = verify(annotatedSource);
		java.lang.Class<?> sourceClass = annotatedSource.getClass();
		List<java.lang.reflect.Method> methods = Arrays.stream(sourceClass.getDeclaredMethods())
				.filter(method -> hasAnnotation(method, Method.class))
				.collect(toList());
		List<String> methodNames = methods.stream()
				.map(this::toName)
				.collect(toList());
		List<com.minelx.scriptbuilder.script.attribute.function.Method> scriptMethods =
				methods.stream()
						.map(method -> toScriptMethod(annotatedSource, method))
						.collect(toList());
		for (int at = 0; at < methodNames.size(); at++) {
			result.set(methodNames.get(at), scriptMethods.get(at));
		}
//		result.setAttributes(methodNames, scriptMethods);
		return new AttributeBundle(result, className);
	}

	private com.minelx.scriptbuilder.script.attribute.function.Method toScriptMethod(Object instance, java.lang.reflect.Method method) {
		method.setAccessible(true);
		return new com.minelx.scriptbuilder.script.attribute.function.Method(new Function() {

			@Override
			public IAttribute execute(Params params) {
				try {
					return (IAttribute) method.invoke(instance, params);
				} catch (IllegalAccessException | InvocationTargetException e) {
					e.printStackTrace();
				}
				return Function.doNothing();
			}
		});
	}

	private String toName(java.lang.reflect.Method method) {
		Method annotation = method.getAnnotation(Method.class);
		return annotation.methodName().equals("undefined")
				? method.getName()
				: annotation.methodName();
	}

	private boolean hasAnnotation(java.lang.reflect.Method method, java.lang.Class<Method> annotation) {
		return method.getAnnotation(annotation) != null;
	}

	private String verify(Object annotatedSource) {
		java.lang.Class<?> aClass = annotatedSource.getClass();
		Clazz classAnnotation = aClass.getAnnotation(Clazz.class);
		if (classAnnotation == null)
			throw new IllegalArgumentException("this object is not annotated by Clazz");
		return classAnnotation.className().equals("undefined")
				? aClass.getSimpleName()
				: classAnnotation.className();
	}

	public static class AttributeBundle {

		private final IAttribute attribute;

		private final String attributeName;

		public AttributeBundle(IAttribute attribute, String attributeName) {
			this.attribute = attribute;
			this.attributeName = attributeName;
		}

		public String getAttributeName() {
			return attributeName;
		}

		public IAttribute getAttribute() {
			return attribute;
		}
	}
}
